//
//  MadokaTextField.swift
//  TextFieldEffects
//
//  Created by Raúl Riera on 05/02/2015.
//  Copyright (c) 2015 Raul Riera. All rights reserved.
//

import UIKit

/**
 A MadokaTextField is a subclass of the TextFieldEffects object, is a control that displays an UITextField with a customizable visual effect around the edges of the control.
 */
open class MadokaTextField: TextFieldEffects {
  /// The color of the placeholder text.
  ///
  /// This property applies a color to the complete placeholder string. The default value for this property is #3D3D424C.
  open var placeholderColor: UIColor = UIColor(red: 0.24, green: 0.24, blue: 0.26, alpha: 0.3) {
    didSet {
      updatePlaceholder()
    }
  }
  
  /// The color of the border.
  ///
  /// This property applies a color to the bounds of the control. The default value for this property is a clear color.
  open var borderColor: UIColor? {
    didSet {
      updateBorder()
    }
  }
  
  /// The scale of the placeholder font.
  ///
  /// This property determines the size of the placeholder label relative to the font size of the text field.
  open var placeholderFontScale: CGFloat = 0.65 {
    didSet {
      updatePlaceholder()
    }
  }
  
  open override var placeholder: String? {
    didSet {
      updatePlaceholder()
    }
  }
  
  open override var bounds: CGRect {
    didSet {
      updateBorder()
      updatePlaceholder()
    }
  }
  
  private let borderThickness: CGFloat = 1
  private let placeholderInsets = CGPoint(x: 6, y: 6)
  private let textFieldInsets = CGPoint(x: 6, y: 6)
  private let borderLayer = CAShapeLayer()
  private var backgroundLayerColor: UIColor?
  
  // MARK: - TextFieldEffects
  
  open override func drawViewsForRect(_ rect: CGRect) {
    let frame = CGRect(origin: CGPoint.zero, size: CGSize(width: rect.size.width, height: rect.size.height))
    
    placeholderLabel.frame = frame.insetBy(dx: placeholderInsets.x, dy: placeholderInsets.y)
    placeholderLabel.font = placeholderFontFromFont(font)
    
    updateBorder()
    updatePlaceholder()
    
    layer.addSublayer(borderLayer)
    addSubview(placeholderLabel)
  }
  
  override open func animateViewsForTextEntry() {
    UIView.animate(withDuration: 0.3, animations: {
      self.updatePlaceholderTransform()
    }) { _ in
      self.animationCompletionHandler?(.textEntry)
    }
  }
  
  open override func animateViewsForTextDisplay() {
    guard text?.isEmpty == true else { return }
    borderLayer.strokeEnd = percentageForBottomBorder()
    UIView.animate(withDuration: 0.3, animations: {
      self.placeholderLabel.transform = .identity
    }) { _ in
      self.animationCompletionHandler?(.textDisplay)
    }
  }
  
  // MARK: - Private
  
  private func updateBorder() {
    let rect = rectForBorder(bounds)
    let path = UIBezierPath()
    path.move(to: CGPoint(x: rect.origin.x + borderThickness, y: rect.height - borderThickness))
    path.addLine(to: CGPoint(x: rect.width - borderThickness, y: rect.height - borderThickness))
    path.addLine(to: CGPoint(x: rect.width - borderThickness, y: rect.origin.y + borderThickness))
    path.addLine(to: CGPoint(x: rect.origin.x + borderThickness, y: rect.origin.y + borderThickness))
    path.close()
    borderLayer.path = path.cgPath
    borderLayer.lineCap = .square
    borderLayer.lineWidth = borderThickness
    borderLayer.fillColor = nil
    borderLayer.strokeColor = borderColor?.cgColor
    borderLayer.strokeEnd = percentageForBottomBorder()
  }
  
  private func percentageForBottomBorder() -> CGFloat {
    let borderRect = rectForBorder(bounds)
    let sumOfSides = borderRect.width * 2 + borderRect.height * 2
    return borderRect.width / sumOfSides
  }
  
  private func updatePlaceholder() {
    print(#function, #line)
    placeholderLabel.text = placeholder
    placeholderLabel.textColor = placeholderColor
    placeholderLabel.sizeToFit()
    layoutPlaceholderInTextRect()
    
    if isFirstResponder || text?.isEmpty == false {
      updatePlaceholderTransform()
    }
  }
  
  private func updatePlaceholderTransform() {
    borderLayer.strokeEnd = 1.0
    let scaleX = 0.8
    let translationX = -placeholderLabel.bounds.width * (1 - scaleX) / 2
    let translationY = placeholderInsets.y + placeholderLabel.bounds.height
    let translate = CGAffineTransform(translationX: translationX, y: translationY)
    let scale = CGAffineTransform(scaleX: scaleX, y: scaleX)
    placeholderLabel.transform = scale.concatenating(translate) // 先位移，后缩放
  }
  
  private func placeholderFontFromFont(_ font: UIFont?) -> UIFont {
    guard let font else { return UIFont() }
    return UIFont(descriptor: font.fontDescriptor, size: font.pointSize * placeholderFontScale)
  }
  
  private func rectForBorder(_ bounds: CGRect) -> CGRect {
    let lineHeight = font?.lineHeight ?? 0.0
    let newRect = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height - lineHeight + textFieldInsets.y)
    
    return newRect
  }
  
  private func layoutPlaceholderInTextRect() {
    placeholderLabel.transform = CGAffineTransform.identity
    
    let textRect = self.textRect(forBounds: bounds)
    var originX = textRect.origin.x
    switch textAlignment {
    case .center:
      originX += textRect.size.width / 2 - placeholderLabel.bounds.width / 2
    case .right:
      originX += textRect.size.width - placeholderLabel.bounds.width
    default:
      break
    }
    
    placeholderLabel.frame = CGRect(
      x: originX,
      y: textRect.height - placeholderLabel.bounds.height - placeholderInsets.y,
      width: placeholderLabel.bounds.width,
      height: placeholderLabel.bounds.height
    )
  }
  
  // MARK: - Overrides
  
  override open func editingRect(forBounds bounds: CGRect) -> CGRect {
    let newBounds = rectForBorder(bounds)
    return newBounds.insetBy(dx: textFieldInsets.x, dy: 0)
  }
  
  override open func textRect(forBounds bounds: CGRect) -> CGRect {
    let newBounds = rectForBorder(bounds)
    return newBounds.insetBy(dx: textFieldInsets.x, dy: 0)
  }
}
